/*
 * usb_utility.cpp
 *
 *  Created on: 14.02.2009
 *      Author: fregat
 */

//# include <logger.h>
# include "usb_utility.h"



using namespace fregat::microchip::usb;
using namespace fregat::utility::logger;


void
usb_initialize ()
{
	usb_init ();
	usb_find_busses();
	usb_find_devices();
}

usbEnvironment::usbEnvironment () {
	pbc = new busCollector ();
};

void
usbEnvironment::readConfiguration() {
	usb_set_debug(50);
	DEBUG("before usb_init()");
	usb_init();
	DEBUG("before usb_find_busses()");
	usb_find_busses();
	DEBUG("before usb_find_devices()");
	usb_find_devices();

	DEBUG("before \"for\" loop");
	fregat::microchip::usb::bus *pb;
	do {
		DEBUG("before usb_get_busses()");
		usb_bus_t *pbb = usb_get_busses();

		DEBUG("after usb_get_busses() %p", pbb);
		if(pbb != 0) {
			INFO("pbb->next = %p; pbb->prev = %p; pbb->dirname = %s; pbb->location = %ld; pbb->devices = %p; pbb->root_dev = %p",
					pbb->next, pbb->prev, pbb->dirname, pbb->location, pbb->devices, pbb->root_dev);
		}
		else {
			ISSUE("Some problem during usb_get_busses call. There are not a usb busses in the system? pbb = 0");
			return;
		}
		pb = new fregat::microchip::usb::bus(*pbb);
		DEBUG("before readconfiguration %p", pb);
		pb->readConfiguration();
		pb->collect_channels(usb_channels);
		pbc->add(pb);

	} while (pb->getNext());
	DEBUG("after loop");
}


boost::shared_ptr <usb_channel>
usbEnvironment::findDevice(vendor_t v, productid_t p) {
	usb_device_descriptor_t value = -1;

	std::list<device *> result;
	busCollector::iterate_f itfunc = boost::bind(&usbEnvironment::collect_according_vendor, this, _1, v, p, boost::ref(result));
	pbc->iterateWith(itfunc/*boost::bind(
			&usbEnvironment::collect_according_vendor,
			this, _1, v, p, boost::ref(result))*/);
	DEBUG("result.size = %d;", result.size());
	if (result.size() == 0) {
		ISSUE("Device vendor_id: %d, product_id: %d not found", v, p);
		throw device_not_found(v, p);
	}

	if (result.size() > 0) {
		device *pd = *result.begin();
		inpoint_bulk *pin_bulk = pd->findInBulk();
		outpoint_bulk *pout_bulk = pd->findOutBulk();
		boost::shared_ptr<usb_channel> pch(new usb_channel(pd));
		pch->outp = pout_bulk;
		pch->pinp = pin_bulk;
		usb_channels[pch->id] = pch;
		value = pch->id;
	}
	INFO("usb_channels.size () = %d;", usb_channels.size());
	return usb_channels[usb_channels.size() + 1];
}

int
usbEnvironment::open_device(usb_device_descriptor_t d, config_t c, interface_t i) {
	boost::shared_ptr<usb_channel> ch = findChannel(d);
	INFO("Channel with descriptor %d found. State %d.", d, ch->getState());
	if (ch->getState() == error_) {
		ISSUE ("Channel with descriptor %d in ERROR state.", d);
		throw channel_invalid_state ();
	}
	if (ch->getState() == initial || ch->getState () == closed) {
		ch->pd->open(c,i);
		ch->open();
	}
	return 0;
}


int usbEnvironment::write_device(const std::vector<unsigned char> &data, const usb_device_descriptor_t &d) {
	boost::shared_ptr<usb_channel> ch = findChannel(d);
	if (ch == 0)
		throw channel_not_found (d);
	if (ch->getState() == error_) {
		std::stringstream ss;
		ss << __FILE__ << ":" << __LINE__ << "Channel with descriptor " << d
				<< " in ERROR state." << std::endl;
		throw channel_invalid_state ();
	}
	if (ch->getState() == opened)
		ch->outp->write(data, 5000);
	return 0;
}

size_t usbEnvironment::read_device(std::vector<unsigned char> &data, const usb_device_descriptor_t &d) {
	boost::shared_ptr<usb_channel> ch = findChannel(d);
	if (ch->getState() == error_) {
		std::stringstream ss;
		ss << __FILE__ << ":" << __LINE__ << "Channel with descriptor " << d
				<< " in ERROR state." << std::endl;
		throw channel_invalid_state ();
	}
	if (ch->getState() == opened) {
		std::cerr << "Device opened" << std::endl;
		return ch->pinp->read(data, 5000);
	}
	std::cerr << "Device not opened" << std::endl;
	throw read_from_device_failed (99);
}

boost::shared_ptr<usb_channel> usbEnvironment::findChannel(usb_device_descriptor_t dd) {
	INFO ("findChannel : map.size = %d;", usb_channels.size());

	for (std::map<int, boost::shared_ptr<usb_channel> >::iterator iter =
			usb_channels.begin(); iter != usb_channels.end(); ++iter) {
		boost::shared_ptr<usb_channel> ch = (*iter).second;
		INFO ("ch.vid = %d; ch.pid = %d;", ch->vid, ch->pid );
		if (ch->id == dd) {
			return ch;
		}
	}
	throw descriptor_not_found(dd);
}

//boost::shared_ptr<usb_channel_t> usbEnvironment::findChannel(usb_device_descriptor_t d) {
//
//	for (std::map<int, boost::shared_ptr<usb_channel_t> >::iterator iter =
//			usb_channels.begin(); iter != usb_channels.end(); ++iter) {
//		boost::shared_ptr<usb_channel_t> ch = (*iter).second;
//		if (ch->id == d)
//			return ch;
//	}
//	throw channel_not_found(d);
//}

void usbEnvironment::collect_according_vendor(bus *pb, vendor_t v, productid_t p, std::list<device *> &result) {
	try {
		device *pd = pb->findDevice(v, p);
		if (pd)
			result.push_back(pd);
	} catch (device_not_found &dnfex) {
	}
}



void
bus::readConfiguration ()
{
	if (devices == 0) {
		devices_not_found dnf;
		ISSUE(dnf.to_string());
		throw dnf;
	}

	INFO("bus::readConfiguration %p %p", pdc, devices);
	device *pd = new device (*devices, this);
	INFO("bus::readConfiguration %p", pd);
	pdc->add (pd);
	DEBUG("before readconfiguration");
	pd->readConfiguration();
	while (pd->next) {
		pd = new device (*pd->next, this);
		pd->readConfiguration();
		pdc->add (pd);
	}
}

bus::bus (usb_bus_t b) : usb_bus_t (b)
{
	INFO("constructor bus::bus %p", b);
	pdc = new deviceCollector (this);
}

device::device (usb_device_t d, fregat::microchip::usb::bus *pb) : usb_device_t (d),
pcc (new configCollector ()), pec (new endpointCollector), pdh (0), pbus (pb)
{
}

config::config (usb_config_descriptor_t c, device *pd) : usb_config_descriptor_t (c), pdevice (pd)
{
	pic = new interfaceCollector ();
}

interface_::interface_ (usb_interface_descriptor uid, config *pc) :
	usb_interface_descriptor (uid), pconfig (pc)
{
	pec = new endpointCollector ();
}

endpoint::endpoint (usb_endpoint_descriptor ued, interface_ *pi) :
	usb_endpoint_descriptor (ued), pinterface (pi)
{
}

void
device::readConfiguration ()
{
	for (usb_config_descriptor *pcd = config; pcd != config + descriptor.bNumConfigurations; ++pcd) {
		fregat::microchip::usb::config *pc = new fregat::microchip::usb::config(*pcd, this);
		pc->readConfiguration(*pec);
		pcc->add (pc);
	}

}

void
config::readConfiguration (endpointCollector &pec)
{
	for (usb_interface *pi = interface;	pi != interface + bNumInterfaces; ++pi) {
		fregat::microchip::usb::interface_ *pid = new fregat::microchip::usb::interface_ (*(pi->altsetting), this);
		pid->readConfiguration (pec);
		pic->add (pid);
	}
}

void
interface_::readConfiguration (endpointCollector &pec_)
{
	for (usb_endpoint_descriptor *ped = endpoint; ped != endpoint + bNumEndpoints; ++ped) {
		fregat::microchip::usb::endpoint *pep;
		volatile int endpoint_type = USB_ENDPOINT_TYPE_MASK & ped->bmAttributes;
		if (endpoint_type == USB_ENDPOINT_TYPE_BULK)
			if (USB_ENDPOINT_DIR_MASK & ped->bEndpointAddress)
				pep = new fregat::microchip::usb::inpoint_bulk (*ped, this);
			else
				pep = new fregat::microchip::usb::outpoint_bulk (*ped, this);
		else
			pep = new fregat::microchip::usb::endpoint (*ped, this);
		pec->add (pep);
		pec_.add (pep);
	}
}

void
bus::collect_channels (channels_map_t  &d) const {
	for (std::list <device *>::iterator piter = pdc->begin (); piter != pdc->end (); ++piter) {
		device *pd = *piter;
		inpoint_bulk *pin_bulk = pd->findInBulk();
		outpoint_bulk *pout_bulk = pd->findOutBulk();
		boost::shared_ptr<usb_channel> pch(new usb_channel(pd));
		pch->id = d.size() + 1;
		pch->pd = pd;
		pch->outp = pout_bulk;
		pch->pinp = pin_bulk;
		pch->vid = pd->descriptor.idVendor;
		pch->pid = pd->descriptor.idProduct;
		pch->state = initial;
		d[pch->id] = pch;
	}
}

device *
bus::findDevice(vendor_t v, productid_t pid)
{
	std::list <device *>::iterator piter = std::find_if ( pdc->begin (), pdc->end (),
		boost::bind (&device::compare_vendor, _1, v, pid));
	if (piter == pdc->end()) {
		device_not_found dnf(v,pid);
		ISSUE(dnf.to_string());
		throw dnf;
	}
	return *piter;
}

endpoint *
device::findBulkIf (boost::function <bool (endpoint *pep)> f) {
	for (std::list <endpoint *>::iterator piter = pec->begin ();
		piter != pec->end (); ++piter) {
		if (f (*piter))
			return *piter;
	}
	return 0;
}

inpoint_bulk *
device::findInBulk ()
{
	inpoint_bulk_search_visitor insv;
	for (std::list <endpoint *>::iterator piter = pec->begin ();
		piter != pec->end (); ++piter) {

		(*piter)->accept(&insv);
	}
	if (insv.size() == 0)
		throw inbulk_not_found (descriptor.idVendor, descriptor.idProduct);
	return *insv.begin();
}

outpoint_bulk *
device::findOutBulk ()
{
	outpoint_bulk_search_visitor outsv;
	for (std::list <endpoint *>::iterator piter = pec->begin ();
		piter != pec->end (); ++piter) {

		(*piter)->accept(&outsv);
	}
	if (outsv.size() == 0)
		throw outbulk_not_found (descriptor.idVendor, descriptor.idProduct);
	return *outsv.begin();
}


void
device::open (config_t c, interface_t i)
{
	pdh = usb_open(this);
	if (pdh == 0)
		throw device_not_opened (descriptor.idVendor, descriptor.idProduct);

	if(usb_set_configuration(pdh, c) < 0) {
		close ();
		throw config_invalid ();
	}

	int retval = usb_claim_interface (pdh, i /*ep->getInterface()->bInterfaceNumber*/);
	if (retval < 0) {
		close ();
		throw claim_interface_invalid (retval);
	}
}

void
device::close ()
{
	usb_close (pdh);
}

void
device::write (const std::vector<unsigned char> &v, endpoint *ep, timeoutms_t to)
{
//	if(usb_set_configuration(pdh, 1) < 0) {
//		usb_close (pdh);
//		throw config_invalid ();
//	}
//
//	int retval = usb_claim_interface (pdh, ep->getInterface()->bInterfaceNumber);
//	if (retval < 0) {
//		usb_close (pdh);
//		throw claim_interface_invalid (retval);
//	}

	usb_bulk_write (pdh, ep->bEndpointAddress, (char*)&(*v.begin()), v.size(), to );

}

//void write (const std::vector<unsigned char> &v, endpoint *ep, timeoutms_t to);

size_t
device::read (std::vector <unsigned char> &v, endpoint *ep, timeoutms_t to)
{
	int retval = usb_claim_interface (pdh, ep->getInterface()->bInterfaceNumber);
	if (retval < 0) {
		close ();
		throw claim_interface_invalid (retval);
	}

	retval = usb_bulk_read (pdh, ep->bEndpointAddress, (char*)&(*v.begin()), v.size(), to );
	if (retval < 0) {
		close ();
		throw read_from_device_failed (retval);
	}
	std::cerr << "retval = " << std::dec << retval << std::endl;
	return retval;
}


void
outpoint_bulk::write (const std::vector <unsigned char> &v, timeoutms_t to)
{
	device *pd = getInterface()->getConfig()->getDevice();
	pd->write (v, this, to);
}

size_t
inpoint_bulk::read (std::vector <unsigned char> &v, timeoutms_t to)
{
	device *pd = getInterface ()->getConfig ()->getDevice ();
	return pd->read (v, this, to);
}


